package com.hwtx.form.util;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.google.common.collect.Maps;
import com.hwtx.form.domain.FormConstants;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Param {
    static Map<FormConstants.OperatorType, OperatorHandle> mapping = Maps.newHashMap();

    static {
        mapping.put(FormConstants.OperatorType.EQ, new OperatorHandle.EqualHandle());
        mapping.put(FormConstants.OperatorType.NOT_EQ, new OperatorHandle.NotEqualHandle());
        mapping.put(FormConstants.OperatorType.LIKE, new OperatorHandle.LikeHandle());
        mapping.put(FormConstants.OperatorType.GT, new OperatorHandle.GreaterThanHandle());
        mapping.put(FormConstants.OperatorType.GE, new OperatorHandle.GreaterThanOrEqualHandle());
        mapping.put(FormConstants.OperatorType.LT, new OperatorHandle.LessThanHandle());
        mapping.put(FormConstants.OperatorType.LE, new OperatorHandle.LessThanOrEqualHandle());
        mapping.put(FormConstants.OperatorType.IS_NULL, new OperatorHandle.IsNullHandle());
        mapping.put(FormConstants.OperatorType.IS_NOT_NULL, new OperatorHandle.IsNotNullHandle());
        mapping.put(FormConstants.OperatorType.ANY, new OperatorHandle.ANYHandle());
    }

    String name;
    String field;
    String operator;
    String remark;
    List<Condition> conditions;
    Boolean required;
    String type;
    Boolean canEdit;

    private static String getOpExpr(String field, String operator, Object value, boolean isConst) {
        if (StringUtils.isEmpty(operator)) {
            return buildExpr(field, FormConstants.OperatorType.EQ.getSymbol(), value, isConst);
        }
        return buildExpr(field, Objects.requireNonNull(FormConstants.OperatorType.of(operator)).getSymbol(), value, isConst);
    }

    public static String buildExpr(String field, String operator, Object value, boolean isConst) {
        if (Objects.equals(operator, FormConstants.OperatorType.IN.getSymbol())) {
            return field + " " + operator + " (" + ((Collection<?>) value).stream().map(v -> "?").collect(Collectors.joining(",")) + ")";
        }
        return field + " " + operator + (isConst ? value : "?");
    }

    public static ExprRet getExpression(Param param, Condition condition, Object value) {
        FormConstants.OperatorType coperator = FormConstants.OperatorType.of(condition.cOperator);
        if (coperator == null) {
            throw new RuntimeException("不支持的操作符,operator = " + condition.cOperator);
        }
        OperatorHandle operatorHandle = mapping.get(coperator);
        if (operatorHandle == null) {
            throw new RuntimeException("不支持的操作符处理程序,operator = " + condition.cOperator);
        }
        if (value instanceof String) {
            value = StringUtils.isBlank(value.toString()) ? null : value;
        }
        return operatorHandle.getExpression(condition, value, param);
    }

    public boolean isEmpty() {
        return StringUtils.isBlank(name) && StringUtils.isBlank(field) && StringUtils.isBlank(operator) && StringUtils.isBlank(remark)
                && (conditions == null || conditions.isEmpty()) && canEdit == null;
    }

    interface OperatorHandle {

        ExprRet getExpression(Condition condition, Object value, Param param);

        class IsNullHandle implements OperatorHandle {
            @Override
            public ExprRet getExpression(Condition condition, Object value, Param param) {
                String field = param.getField();
                String op = param.getOperator();
                if (value == null) {
                    if (StringUtils.isBlank(condition.rValue) || condition.rValue.equalsIgnoreCase("null")) {
                        return ExprRet.builder().expr(field + " " + FormConstants.OperatorType.IS_NULL.getSymbol()).build();
                    }
                    return ExprRet.builder().expr(getOpExpr(field, condition.rOperator, condition.rValue, true)).build();
                }
                return ExprRet.builder().expr(getOpExpr(field, op, value, false)).param(true).build();
            }
        }

        class IsNotNullHandle implements OperatorHandle {
            @Override
            public ExprRet getExpression(Condition condition, Object value, Param param) {
                String field = param.getField();
                if (value != null) {
                    if (StringUtils.isBlank(condition.rValue)) {
                        return ExprRet.builder().expr(field + FormConstants.OperatorType.IS_NOT_NULL.getSymbol()).build();
                    }
                    return ExprRet.builder().expr(getOpExpr(field, condition.rOperator, condition.rValue, true)).build();
                }
                return null;
            }
        }

        class EqualHandle implements OperatorHandle {
            @Override
            public ExprRet getExpression(Condition condition, Object value, Param param) {
                String field = param.getField();
                String op = param.getOperator();
                if (StringUtils.isBlank(condition.rValue) && Objects.equals(value.toString(), condition.cValue)) {
                    return ExprRet.builder().expr(getOpExpr(field, condition.rOperator, condition.rValue, true)).build();
                }
                if (value != null) {
                    return ExprRet.builder().expr(getOpExpr(field, op, value, false)).param(true).build();
                }
                return null;
            }
        }

        class ANYHandle implements OperatorHandle {
            @Override
            public ExprRet getExpression(Condition condition, Object value, Param param) {
                String field = param.getField();
                value = condition.getRValue();
                if (Types.isString(param.getType())) {
                    value = "'" + condition.getRValue() + "'";
                }
                return ExprRet.builder().expr(getOpExpr(field, FormConstants.OperatorType.EQ.getSymbol(), value, true)).param(false).build();
            }
        }

        class NotEqualHandle implements OperatorHandle {
            @Override
            public ExprRet getExpression(Condition condition, Object value, Param param) {
                String field = param.getField();
                String op = param.getOperator();
                if (StringUtils.isNotBlank(condition.rValue) && value != null && !Objects.equals(value.toString(), condition.cValue)) {
                    return ExprRet.builder().expr(getOpExpr(field, condition.rOperator, condition.rValue, true)).build();
                }
                if (value != null) {
                    return ExprRet.builder().expr(getOpExpr(field, op, value, false)).param(true).build();
                }
                return null;
            }
        }

        class LikeHandle implements OperatorHandle {
            @Override
            public ExprRet getExpression(Condition condition, Object value, Param param) {
                String field = param.getField();
                String op = param.getOperator();
                if (value != null) {
                    if (value.toString().contains(condition.cValue)) {
                        return ExprRet.builder().expr(getOpExpr(field, condition.rOperator, condition.cValue, true)).build();
                    }
                    return ExprRet.builder().expr(getOpExpr(field, op, "%" + value + "%", true)).param(true).build();
                }
                return null;
            }
        }

        class GreaterThanHandle implements OperatorHandle {
            @Override
            public ExprRet getExpression(Condition condition, Object value, Param param) {
                String field = param.getField();
                String op = param.getOperator();
                if (value != null) {
                    if (value.toString().compareTo(condition.cValue) > 0) {
                        return ExprRet.builder().expr(getOpExpr(field, condition.rOperator, condition.cValue, true)).build();
                    }
                    return ExprRet.builder().expr(getOpExpr(field, op, value, false)).param(true).build();
                }
                return null;
            }
        }

        class GreaterThanOrEqualHandle implements OperatorHandle {
            @Override
            public ExprRet getExpression(Condition condition, Object value, Param param) {
                String field = param.getField();
                String op = param.getOperator();
                if (value != null) {
                    if (value.toString().compareTo(condition.cValue) >= 0) {
                        return ExprRet.builder().expr(getOpExpr(field, condition.rOperator, condition.cValue, true)).build();
                    }
                    return ExprRet.builder().expr(getOpExpr(field, op, value, false)).param(true).build();
                }
                return null;
            }
        }

        class LessThanHandle implements OperatorHandle {
            @Override
            public ExprRet getExpression(Condition condition, Object value, Param param) {
                String field = param.getField();
                String op = param.getOperator();
                if (value != null) {
                    if (value.toString().compareTo(condition.cValue) < 0) {
                        return ExprRet.builder().expr(getOpExpr(field, condition.rOperator, condition.cValue, true)).build();
                    }
                    return ExprRet.builder().expr(getOpExpr(field, op, value, false)).param(true).build();
                }
                return null;
            }
        }

        class LessThanOrEqualHandle implements OperatorHandle {
            @Override
            public ExprRet getExpression(Condition condition, Object value, Param param) {
                String field = param.getField();
                String op = param.getOperator();
                if (value != null) {
                    if (value.toString().compareTo(condition.cValue) <= 0) {
                        return ExprRet.builder().expr(getOpExpr(field, condition.rOperator, condition.cValue, true)).build();
                    }
                    return ExprRet.builder().expr(getOpExpr(field, op, value, false)).param(true).build();
                }
                return null;
            }
        }
    }

    @Data
    @JsonInclude(JsonInclude.Include.NON_NULL)
    public static class Condition {
        String cOperator;
        String cValue;
        String rOperator;
        String rValue;
    }

    @Getter
    @Builder
    public static class ExprRet {
        String expr;
        boolean param;
    }
}